var HTTP_OK = 200;

var milisecondsToMinutes = 1000 * 60;

/** Creates a new NewsHeader with the given parameters.
 * @constructor
 * @author Alin Danciu alin.danciu@gmail.com
 * @class Encapsulates information about a news entry in a RSS.
 */
function NewsHeader(titleP, urlP, descriptionP, wordsP)
{
	this.title = titleP;
	this.url = urlP;
	this.description = descriptionP;
	this.words = wordsP;
}

NewsHeader.prototype.toString = function() {
	return this.title + "\n" + this.url + "\n";
}

/** Creates a new NewsHeaderList containing no elements.
 * @constructor
 * @param {Integer} the size of the underlying array
 * @author Alin Danciu alin.danciu@gmail.com
 * @class Encapsulates a number of {@link NewsHeader} items.
 */
function NewsHeaderList()
{
	this.items = new Array();
}

/** Adds an item to this list
 * @return true if the item was successfully added to the list
 * @throws {String} if the argument is not a NewsHeaderList
 * @member NewsHeaderList
 * @author Alin Danciu alin.danciu@gmail.com
 * @requires NewsHeader
 */
function news_header_list_add(item) {
	if((item instanceof NewsHeader) == true) {
		this.items.push(item);
		return true;
	} else {
		throw "Bad element type in call to add method";
	}
}

/** Gets the number of items in the list
 * @return the number of items in the list
 * @member NewsHeaderList
 * @author Alin Danciu alin.danciu@gmail.com
 */
function news_header_list_length() {
	return this.items.length;
}

NewsHeaderList.prototype.add = news_header_list_add;
NewsHeaderList.prototype.length =  news_header_list_length;
NewsHeaderList.prototype.toString = function() {
	res = this.items.length + " entries\n";
	for(idx in this.items) {
		res += this.items[idx].toString() + "\n";
	}
	return res;
}

/** Constructs a new RSSSource with the specified url.
 * All other fields are undefined
 * @constructor
 * @param {String} urlP the url of this RSS channel
 * @class This class encapsulates information about a RSS chanel. It is used
 * to retrive the RSS information.
 * @author Alin Danciu alin.danciu@gmail.com
 */
function RSSSource(urlP) {
	this.url = urlP;
	this.title = null;
	this.timeStamp = null;
	this.news = null;
}

/** Gets a RSS from the url specified at construction of the RSSSource object
 * @return an responseXML object containing the parsed XMLDocument
 * @throws {String} if the data could not be retrived
 * @throws {String} if the retrived data is not an XMLDocument
 * @member RSSSource
 * @author Alin Danciu alin.danciu@gmail.com
 * @requires XMLHttpRequest
 */
function rssSource_getRSS() {
	//create a new request object for this RSS
	var request = new XMLHttpRequest();
	
	//send the request for the url
	request.open("GET", this.url, false);
	request.send();
	
	//set the date and time of the GET operation
	this.timeStamp = new Date();
	
	//check the status of the request and return the result
	if(request.status == HTTP_OK) {
		if(request.responseXML != null) {
			return request.responseXML;
		} else {
			throw "The result of get operation is not a valid XML";
		}
	} else {
		throw "Unable to get the specified url";
	}
}

/** Gets the news entries in this RSSSource
 * @return a {@link NewsHeaderList} containing all the entries
 * @member RSSSource
 * @author Alin Danciu alin.danciu@gmail.com
 * @requires NewsHeaderList
 */
function rssSource_getNews() {
	//we called get news before
	if(this.timeStamp != null) {
		//calculate the elapsed time in milliseconds
		var current = new Date();
		var difference = (current - this.timeStamp) / milisecondsToMinutes;
		//we can request the news again
		if(difference > this.refreshTime) {
			//it should be safe to assume that this RSS has not changed but anyway
			try {
				return this.parseNews(this.getRSS());
			}catch(e) {
				alert(e.message);
				//try to return whatever was stored in the news before
				return this.news;
			}
		} else {
			//return the current news
			return this.news;
		}
	} else {
		return this.parseNews(this.getRSS());
	}
}

function rssSource_parseNews(document) {
	//get the channel node
	var channelList = document.getElementsByName("channel");
	if(channelList.length == 1) {
		var channelTitles = channelList.item(0).getElementsByTagName("title");
		this.title = channelTitles.item(0).firstChild.data;
	} else {
		alert("invalid RSS strucutre");
		return null;
	}
	var items = channelList.item(0).getElementsByTagName("item");
	this.news = new NewsHeaderList();
	for(i = 0;i < items.length;i++) {
		entry = parseRssEntry(items.item(i));
		if(entry != null) {
			this.news.add(entry);
		}
	}
	return this.news;
}

function parseRssEntry(node) {
	var title;
	var link;
	var description;
	var wordsNode;
	
	try {
		//if there is only one node
		if(node.childNodes.length == 1) {
			//this means no results were found so we create a standard no results news
			return new NewsHeader(node.firstChild.firstChild.data, null, null, null);
		} else {
			//this node is a valid item node in the RSS
			title = node.getElementsByTagName("title").item(0).firstChild;
			link = node.getElementsByTagName("link").item(0).firstChild;
			description = node.getElementsByTagName("description").item(0).firstChild;
			wordsNode = node.getElementsByTagName("keywords");
			if(wordsNode != null) {
				var words = wordsNode.item(0).firstChild;
			}
			return new NewsHeader(title.data, link.data, description.data, words.data);
		}
		//try to get the title child of this node
		
	} catch(exception)
	{
		alert(exception.message);
		return null;
	}
}

RSSSource.prototype.getRSS = rssSource_getRSS;
RSSSource.prototype.getNews = rssSource_getNews;
RSSSource.prototype.parseNews = rssSource_parseNews;
RSSSource.prototype.refreshTime = 30;

/** Creates a new RSSCache with the given initial size.
 * @constructor
 * @author Alin Danciu alin.danciu@gmail.com
 * @class Encapsulates information about a cache of RSSSources.
 */
function RSSCache(size) {
	this.items = new Array(size);
}

/** Gets the news list for a given RSSSource contained in this cache. If the entry is not found a new RSSSource is created 
 *	and the content is retrived from the internet. The entry is added to the cache.
 * @return an NewsHeaderList containing all the news for the given url.
 * @member RSSCache
 * @author Alin Danciu alin.danciu@gmail.com
 * @requires RSSSource
 */
function rssCache_get(url) {
	for(item in this.items) {
		if(this.items[item].url == url) {
			return this.items[item].news;
		}
	}
	rss =  new RSSSource(url);
	try {
		var news = rss.getNews();
		this.items.push(rss);
		return news;
	} catch (e) {
		alert(e.message);
	}
}

RSSCache.prototype.get = rssCache_get;